package tool

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/olivere/elastic/v7"
	"net/http"
	"reflect"
	"strconv"
	. "xm_shop/database"
	. "xm_shop/models"
)

type SearchController struct{}

// 创建索引
func (s SearchController) Index(c *gin.Context) {
	// 判断索引是否存在
	exists, err1 := EsClient.IndexExists("goods").Do(context.Background())
	if err1 != nil {
		panic(err1)
	}
	if !exists { // 如果索引不存在
		// 先配置映射
		// number_of_shards 表示分片数
		// number_of_replicas 表示副本数
		mapping := ` {
			"settings": { "number_of_shards": 1,
			"number_of_replicas": 0,
			},
			"mappings": { 
				"properties": { 
					"Content": { "type": "text", "analyzer": "ik_max_word", "search_analyzer": "ik_max_word"},
					"Title": { "type": "text", "analyzer": "ik_max_word", "search_analyzer": "ik_max_word"},
					}
				}
			}`
		//      										IncludeTypeName(true)  表示索引包括类型名, 7.x版本以后, 类型名已被废弃, 默认情况下不再包括类型名
		_, err2 := EsClient.CreateIndex("goods").Body(mapping).IncludeTypeName(true).Do(context.Background())
		if err2 != nil {
			fmt.Println(err2)
			return
		}
		c.String(http.StatusOK, "索引创建成功")
	}
	c.String(http.StatusOK, "索引已存在")
}

// 新增数据到es
func (s SearchController) Add(c *gin.Context) {
	var goods []Goods
	DB.Find(&goods)

	// 指定Id新增es数据(文档)                                           BodyJson表示以json的格式添加数据(文档)
	put1, err1 := EsClient.Index().Index("goods").Id(strconv.Itoa(goods[0].ID)).BodyJson(goods[0]).Do(context.Background()) // 返回文档对象
	if err1 != nil {
		panic(err1)
	}
	fmt.Printf(put1.Id, put1.Index, put1.Type)
	c.String(http.StatusOK, "新增数据成功")

	/*
		// for循环批量插入数据(文档)到es
		for i := 0; i < len(goods); i++ {
			_, err := EsClient.Index().Index("goods").Id(strconv.Itoa(goods[i].ID)).BodyJson(goods[i]).Do(context.Background())
			if err != nil {
				c.String(http.StatusOK, "插入失败")
				return
			}
		}
		c.String(http.StatusOK, "插入成功")

	*/
}

// 更新数据
func (s SearchController) Edit(c *gin.Context) {
	var goods []Goods
	DB.Find(&goods)
	goods[0].Title = "我是修改后的数据"
	// 													Doc写入新数据, 可以是结构体, 也可以是map
	_, err1 := EsClient.Update().Index("goods").Id("19").Doc(goods[0]).Do(context.Background())
	if err1 != nil {
		c.String(http.StatusOK, "修改失败")
		return
	}
	c.String(http.StatusOK, "修改成功")
}

// 删除数据
func (s SearchController) Delete(c *gin.Context) {
	_, err1 := EsClient.Delete().Index("goods").Id("19").Do(context.Background())
	if err1 != nil {
		c.String(http.StatusOK, "删除失败")
	}
	c.String(http.StatusOK, "删除成功")
}

// 获取指定数据
func (s SearchController) GetOne(c *gin.Context) {
	defer func() {
		if err := recover(); err != nil {
			fmt.Println(err)
			c.String(http.StatusOK, "error")
		}
	}()

	result, err1 := EsClient.Get().Index("goods").Id("19").Do(context.Background())
	if err1 != nil {
		panic(err1)
	}
	var goods Goods
	json.Unmarshal(result.Source, &goods) // result.Source获取到数据, 类似是字节切片
	c.JSON(http.StatusOK, gin.H{
		"success": true,
		"goods":   goods,
	})
}

// 模糊查询
func (s SearchController) VagueSearch(c *gin.Context) {
	query := elastic.NewMatchQuery("Title", "手机") // 创建匹配查询对象
	result, err := EsClient.Search().Index("goods").Query(query).Do(context.Background())
	if err != nil {
		c.String(http.StatusOK, "查询失败")
		return
	}
	var goods Goods
	c.JSON(http.StatusOK, gin.H{
		//"result": result,
		"result": result.Each(reflect.TypeOf(goods)), // 原理就是利用反射获取goods对象的类型, 然后在result中遍历, 获取符合类型的数据
	})
}

// 分页查询
func (s SearchController) PaginationSearch(c *gin.Context) {
	query := elastic.NewMatchQuery("Title", "手机") // 创建匹配查询对象
	// From(0).Size(2)  从下标0开始获取2条数据    Sort("ID", true) 按照ID字段排序 true表示升序, false表示降序
	result, err := EsClient.Search().Index("goods").Query(query).Sort("ID", false).From(0).Size(2).Do(context.Background())
	// 这里分页也是 From((page - 1) * pageSize).Size(pageSize)
	if err != nil {
		c.String(http.StatusOK, "查询失败")
		return
	}
	var goods Goods
	c.JSON(http.StatusOK, gin.H{
		"result": result.Each(reflect.TypeOf(goods)), // Each遍历获取到每个符合类型的数据
	})
}

// 条件查询(常用)
func (s SearchController) FilterSearch(c *gin.Context) {
	// 创建筛选对象
	boolQ := elastic.NewBoolQuery()
	// 加入筛选条件
	boolQ.Must(elastic.NewMatchQuery("Title", "小米")) // 模糊查询
	boolQ.Filter(elastic.NewRangeQuery("ID").Gt(19)) // 范围查询
	boolQ.Filter(elastic.NewRangeQuery("ID").Lt(31))
	result, err := EsClient.Search().Index("goods").Query(boolQ).Sort("ID", false).Do(context.Background())
	if err != nil {
		c.String(http.StatusOK, "查询失败")
		return
	}
	var goods Goods
	c.JSON(http.StatusOK, gin.H{
		"result": result.Each(reflect.TypeOf(goods)),
	})
}
